#include <iostream>
#include <stdexcept>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;

namespace jmx
{
    // smart_ptr
    template <class T>
    class smart_ptr
    {
        T *_ptr;

    public:
        // 构造时，将指针保存到对象内部
        smart_ptr(T *ptr)
            : _ptr(ptr)
        {
        }
        // 析构时，释放指针指向的堆空间
        ~smart_ptr()
        {
            cout << "delete _ptr: " << _ptr << endl;
            delete _ptr;
        }
        // 重载*和->使smart_ptr可以向原生指针一样使用
        T &operator*()
        {
            return *_ptr;
        }
        T *operator->()
        {
            return _ptr;
        }
        // 思考一下，使用默认生成的拷贝构造和赋值重载（值拷贝）可以吗？
    };

    // auto_ptr
    template <class T>
    class auto_ptr
    {
        T *_ptr;

    public:
        auto_ptr(T *ptr)
            : _ptr(ptr)
        {
        }

        // auto_ptr的拷贝方法：管理权转移
        auto_ptr(auto_ptr &sp)
            : _ptr(sp._ptr)
        {
            sp._ptr = nullptr; // 将拷贝对象的指针置空
        }

        auto_ptr &operator=(auto_ptr &ap)
        {
            // 赋值重载注意检测是否是自己给自己赋值
            if (_ptr != ap._ptr)
            {
                // 释放当前指针指向的堆空间
                if (_ptr != nullptr)
                {
                    cout << "delete _ptr: " << _ptr << endl;
                    delete _ptr;
                }
                // 转移ap中的资源到当前对象中
                _ptr = ap._ptr;
                ap._ptr = nullptr;
            }
            return *this;
        }

        ~auto_ptr()
        {
            if (_ptr != nullptr)
            {
                cout << "delete _ptr: " << _ptr << endl;
                delete _ptr;
            }
        }

        T &operator*()
        {
            return *_ptr;
        }

        T *operator->()
        {
            return _ptr;
        }
    };

    // unique_ptr
    template <class T>
    class unique_ptr
    {
        T *_ptr;
        // C++98:
        // unique_ptr(const unique_ptr &up);
        // unique_ptr &operator=(const unique_ptr &up);

    public:
        unique_ptr(T *ptr)
            : _ptr(ptr)
        {
        }

        ~unique_ptr()
        {
            if (_ptr != nullptr)
            {
                cout << "delete _ptr: " << _ptr << endl;
                delete _ptr;
            }
        }
        // C++11:
        unique_ptr(const unique_ptr &up) = delete;
        unique_ptr &operator=(const unique_ptr &up) = delete;
    };

    // shared_ptr
    template <class T>
    class shared_ptr
    {
        T *_ptr = nullptr;
        int *_pcount = nullptr;
        mutex *_mtx = nullptr;
        function<void(T *)> _del = [](T *ptr)
        { cout << "lambda: delete ptr" << endl;
            delete ptr; };

    public:
        shared_ptr(T *ptr)
            : _ptr(ptr),
              _pcount(new int(1)),
              _mtx(new mutex)
        {
        }

        template <class D>
        shared_ptr(T *ptr, D del)
            : _ptr(ptr),
              _pcount(new int(1)),
              _mtx(new mutex),
              _del(del)
        {
        }

        void Release()
        {
            bool delete_flag = false;
            _mtx->lock();
            // cout << "void Release()" << endl;
            if (--(*_pcount) == 0 && _ptr != nullptr)
            {
                cout << "_del(_ptr): " << _ptr << endl;
                _del(_ptr); // 使用删除器释放_ptr
                delete _pcount;
                delete_flag = true;
            }
            _mtx->unlock();
            if (delete_flag)
            {
                delete _mtx;
            }
        }

        void AddCount()
        {
            _mtx->lock();
            ++(*_pcount);
            _mtx->unlock();
        }

        shared_ptr(const shared_ptr &sp)
            : _ptr(sp._ptr),
              _pcount(sp._pcount),
              _mtx(sp._mtx),
              _del(sp._del) // 记得拷贝删除器
        {
            AddCount();
        }

        shared_ptr &operator=(const shared_ptr &sp)
        {
            if (_ptr != sp._ptr)
            {
                Release();
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                _mtx = sp._mtx;
                _del = sp._del; // 记得拷贝删除器
                AddCount();
            }
            return *this;
        }

        ~shared_ptr()
        {
            Release();
        }

        T *get()
        {
            return _ptr;
        }

        int use_count()
        {
            return *_pcount;
        }

        T &operator*()
        {
            return *_ptr;
        }

        T *operator->()
        {
            return _ptr;
        }
    };

    template <class T>
    class weak_ptr
    {
        T *_ptr;

    public:
        weak_ptr(shared_ptr<T> sp)
            : _ptr(sp.get())
        {
        }

        T *get()
        {
            return _ptr;
        }

        T &operator*()
        {
            return *_ptr;
        }

        T *operator->()
        {
            return _ptr;
        }
    };
}

int div()
{
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}
// void Func()
// {
//     jmx::unique_ptr<int> p1(new int(1));
//     jmx::unique_ptr<int> p2(new int(2));

//     cout << "*p1 -> " << *p1 << endl;
//     cout << "jmx::auto_ptr<int> p3(p1);" << endl;
//     jmx::unique_ptr<int> p3(p1); // 拷贝构造
//     cout << "*p3 -> " << *p3 << endl;

//     cout << "*p2 -> " << *p2 << endl;
//     cout << "p2 = p3;" << endl;
//     p2 = p3; // 赋值重载
//     cout << "*p2 -> " << *p2 << endl;

//     cout << div() << endl;
// }

// void Func()
// {
//     jmx::shared_ptr<int> p1(new int(1));
//     jmx::shared_ptr<int> p2(p1);
//     cout << *p1 << " " << *p2 << endl;
//     *p1 = 10;
//     *p2 = 20;
//     cout << *p1 << " " << *p2 << endl;
//     cout << div() << endl;
// }
// int main()
// {
//     try
//     {
//         Func();
//     }
//     catch (exception &e)
//     {
//         cout << e.what() << endl;
//     }
//     return 0;
// }

// shared_ptr的线程安全问题
// struct Date
// {
//     int _year = 0;
//     int _month = 0;
//     int _day = 0;
// };

// void ThreadFunc(std::shared_ptr<Date> &sp, int n, mutex &mtx)
// {
//     while (n--)
//     {
//         std::shared_ptr<Date> copy(sp);
//         lock_guard<mutex> lock(mtx);
//         ++copy->_year;
//         ++copy->_month;
//         ++copy->_day;
//     }
// }

// int main()
// {
//     std::shared_ptr<Date> sp(new Date);
//     // cout << sp.get() << endl;
//     cout << sp.get() << endl;
//     int n = 100000;
//     mutex mtx;
//     thread t1(ThreadFunc, ref(sp), n, ref(mtx));
//     thread t2(ThreadFunc, ref(sp), n, ref(mtx));
//     t1.join();
//     t2.join();

//     // cout << sp.use_count() << endl;
//     cout << sp.use_count() << endl;
//     cout << sp->_year << "/" << sp->_month << "/" << sp->_day << endl;
//     return 0;
// }

// shared_ptr的循环引用问题
// template <class T>
// struct ListNode
// {
//     // ListNode *_prev;
//     // ListNode *_next;

//     jmx::weak_ptr<ListNode> _prev;
//     jmx::weak_ptr<ListNode> _next;
//     T _val;

//     ListNode(T val = T())
//         : _prev(nullptr),
//           _next(nullptr),
//           _val(val)
//     {
//     }

//     ~ListNode()
//     {
//         cout << "~ListNode()" << endl;
//     }
// };

// void test_shared_cycle()
// {
//     jmx::shared_ptr<ListNode<int>> node1(new ListNode<int>(1));
//     jmx::shared_ptr<ListNode<int>> node2(new ListNode<int>(2));

//     cout << node1.use_count() << endl;
//     cout << node2.use_count() << endl;
//     node1->_next = node2;
//     node2->_prev = node1;
//     cout << node1.use_count() << endl;
//     cout << node2.use_count() << endl;
// }

// 定制删除器
struct Date
{
    int _year = 0;
    int _month = 0;
    int _day = 0;
    ~Date()
    {
        cout << "~Date()" << endl;
    }
};

struct DatesDeleter
{
    void operator()(Date *ptr)
    {
        cout << "void operator()(Date* ptr): delete[] ptr" << endl;
        delete[] ptr;
    }
};

void test_shared_deleter()
{
    jmx::shared_ptr<Date> sp1(new Date);
    jmx::shared_ptr<Date> sp2(new Date[3], DatesDeleter());
    jmx::shared_ptr<FILE> sp3(fopen("./smart_ptr.cc", "r"), [](FILE *fp)
                              {
        cout << "lambda: fclose(fp)" << endl;
        fclose(fp); });
}

int main()
{
    // test_shared_cycle();
    test_shared_deleter();
    return 0;
}